package exsun.jt808.data.parseUtil;

import exsun.jt808.data.upload_data.*;
import exsun.jt808.data.util.GsonUtils;
import exsun.jt808.data.util.parse.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;


public class GpsUtil {

    private static final Logger logger = LoggerFactory.getLogger(GpsUtil.class);

    public static GpsData parseGps(byte[] bytes,boolean blindSpot){
            GpsData data = new GpsData();
            data.setAlm(GpsUtil.GetAlarm(HexStrUtil.byteArrToBinStr(bytes, 0, 4)));
            data.setAmm(BitOperator.fourBytesToLong(bytes, 0));
            data.setSts(GpsUtil.GetStatus(HexStrUtil.byteArrToBinStr(bytes, 4, 4)));
            if (blindSpot == true){
                data.addSts(GpsStatusType.getBlindSpot());
            }
            data.setStm(BitOperator.fourBytesToLong(bytes, 4));
            data.setLat(Integer.parseInt(HexStrUtil.encodeHex(bytes, 8, 4), 16) / 1000000.0);
            data.setLng(Integer.parseInt(HexStrUtil.encodeHex(bytes, 12, 4), 16) / 1000000.0);
            data.setHgt(HexStrUtil.bytesToInt(bytes, 16, 2));
            data.setSpd(HexStrUtil.bytesToInt(bytes, 18, 2) / 10);
            data.setDre(HexStrUtil.bytesToInt(bytes, 20, 2));
            data.setGte(TimeUtil.parseTime(HexStrUtil.encodeHex(bytes, 22, 6), TimeUtil.dateFormatyyMMddHHmmss));
            data.setGdt(TimeUtil.getDateByFormat(data.getGte(), TimeUtil.dateFormatYMDHMS));
            data.setPte(TimeUtil.getDateByFormat(TimeUtil.getNowTimeStamp(), TimeUtil.dateFormatYMDHMS));
            if (TimeUtil.getOffectMinutes(TimeUtil.getNowTimeStamp(), data.getGte()) > 5 || data.getGte() > TimeUtil.getNowTimeStamp())
            {
                data.setGte(TimeUtil.getNowTimeStamp());
                data.setGdt(TimeUtil.getDateByFormat(data.getGte(), TimeUtil.dateFormatYMDHMS));
            }
            GpsUtil.ParseGpsExtenstionInfo(bytes, data);
            return data;

    }


    /**
     * 获取报警类型
     *
     * @param bin
     * @return
     */
    public static List<Integer> GetAlarm(String bin) {
        List<Integer> alarms = new ArrayList<Integer>();

        // 紧急报警
        if (bin.charAt(31) == '1') alarms.add(GpsAlarmType.Emergency);

        // 超速报警
        if (bin.charAt(30) == '1') alarms.add(GpsAlarmType.OverSpeed);

        // 疲劳驾驶
        if (bin.charAt(29) == '1') alarms.add(GpsAlarmType.FatigueDriving);

        // 预警
        if (bin.charAt(28) == '1') alarms.add(GpsAlarmType.EarlyWarning);

        // GNSS 模块发生故障
        if (bin.charAt(27) == '1') alarms.add(GpsAlarmType.GnssModuleFault);

        // GNSS 天线未接或被剪断
        if (bin.charAt(26) == '1') alarms.add(GpsAlarmType.GnssAntennaLooseOrCutoff);

        // GNSS 天线短路
        if (bin.charAt(25) == '1') alarms.add(GpsAlarmType.GnssAntennaCircuit);

        // 终端主电源欠压
        if (bin.charAt(24) == '1') alarms.add(GpsAlarmType.TerminalMainsUndervoltage);

        // 终端主电源掉电
        if (bin.charAt(23) == '1') alarms.add(GpsAlarmType.TerminalMainsPoweroff);

        // 终端 LCD 或显示器故障
        if (bin.charAt(22) == '1') alarms.add(GpsAlarmType.TerminalMonitorOrLcDfault);

        // TTS 模块故障
        if (bin.charAt(21) == '1') alarms.add(GpsAlarmType.TtsModuleFault);

        // 摄像头故障
        if (bin.charAt(20) == '1') alarms.add(GpsAlarmType.CameraFault);

        // SD 卡异常
        if (bin.charAt(15) == '1') alarms.add(GpsAlarmType.AbnormalSdCard);

        //农机深度传感器异常报警
        if (bin.charAt(14) == '1') alarms.add(GpsAlarmType.AgriculturalMachinery);

        // 当天累计驾驶超时
        if (bin.charAt(13) == '1') alarms.add(GpsAlarmType.CumulativeDrivingTimeout);

        // 超时停车
        if (bin.charAt(12) == '1') alarms.add(GpsAlarmType.OvertimeParking);

        // 进出区域
        if (bin.charAt(11) == '1') alarms.add(GpsAlarmType.InOutOfArea);

        // 进出路线
        if (bin.charAt(10) == '1') alarms.add(GpsAlarmType.InOutOfLine);

        // 路段行驶时间不足/过长
        if (bin.charAt(9) == '1') alarms.add(GpsAlarmType.DrivingTimeLackOrTooLong);

        // 路线偏离报警
        if (bin.charAt(8) == '1') alarms.add(GpsAlarmType.RouteDeviation);

        // 车辆 VSS 故障
        if (bin.charAt(7) == '1') alarms.add(GpsAlarmType.VehicleVssFault);

        // 车辆油量异常
        if (bin.charAt(6) == '1') alarms.add(GpsAlarmType.AbnormalVehicleFuel);

        // 车辆被盗
        if (bin.charAt(5) == '1') alarms.add(GpsAlarmType.VehicleTheft);

        // 车辆非法点火
        if (bin.charAt(4) == '1') alarms.add(GpsAlarmType.IllegalVehicleIgnition);

        // 车辆非法位移
        if (bin.charAt(3) == '1') alarms.add(GpsAlarmType.VehicleIllegalDisplacement);

        // 碰撞侧翻报警
        if (bin.charAt(2) == '1') alarms.add(GpsAlarmType.CollisionRollover);



        return alarms;
    }

//    /**
//     * 视频报警信息
//     */
//    private static List<Integer> ParseForVideoAlarm(String bin){
//        List<Integer> alarms = new ArrayList<Integer>();
//        //特殊报警录像存储阈值报警
//        if (bin.charAt(6) == '1') alarms.add(GpsAlarmType.thresholdAlarm);
//
//        //异常驾驶行为报警
//        if (bin.charAt(5) == '1') alarms.add(GpsAlarmType.abnormalDriving);
//
//        //客车超员报警
//        if (bin.charAt(4) == '1') alarms.add(GpsAlarmType.carOvercrowding);
//
//        //其他视频设备故障报警
//        if (bin.charAt(3) == '1') alarms.add(GpsAlarmType.otherDeviceFailure);
//
//        //存储单元故障报警
//        if (bin.charAt(2) == '1') alarms.add(GpsAlarmType.storageUnitFailure);
//
//        //视频信号遮挡报警
//        if (bin.charAt(1) == '1') alarms.add(GpsAlarmType.videoSignalOcclusion);
//
//        //视频信号丢失报警
//        if (bin.charAt(0) == '1') alarms.add(GpsAlarmType.videoSignalLose);
//
//        return alarms;
//
//    }

    /**
     * 获取状态信息
     *
     * @param bin
     * @return
     */
    public static List<Integer> GetStatus(String bin) {
        List<Integer> status = new ArrayList<Integer>();
        // 0: ACC关 1：ACC开
        if (bin.charAt(31) == '1') status.add(GpsStatusType.Acc);
        // 0: 未定位 1：定位
        if (bin.charAt(30) == '1') status.add(GpsStatusType.Located);

        // 0: 北纬 1：南纬
        if (bin.charAt(29) == '1') status.add(GpsStatusType.LatInfo);

        // 0: 东经 1：西经
        if (bin.charAt(28) == '1') status.add(GpsStatusType.LonInfo);

        // 0: 运营状态 1：停运状态
        if (bin.charAt(27) == '1') status.add(GpsStatusType.BusinessStatus);

        // 0：经纬度未经保密插件加密；1：经纬度已经保密插件加密
        if (bin.charAt(26) == '1') status.add(GpsStatusType.LonLatEncrypt);


        // 0：车辆油路正常  1：车辆油路断开
        if (bin.charAt(21) == '1') status.add(GpsStatusType.OilLine);

        // 0：车辆电路正常  1：车辆电路断开
        if (bin.charAt(20) == '1') status.add(GpsStatusType.ElectricCircuit);


        // 0：车门解锁  1：车门加锁
        if (bin.charAt(19) == '1') status.add(GpsStatusType.DoorLock);

        //0：前门关    1：前门开 （门磁线）
        if (bin.charAt(18) == '1') status.add(GpsStatusType.FrontDoor);

        //0：后门关 或自定义高2无效   1：后门开或自定义高2有效
        if (bin.charAt(17) == '1') status.add(GpsStatusType.BackDoor);

        //0：发动机关  1：发动机开
        if (bin.charAt(16) == '1') status.add(GpsStatusType.Engine);

        //0：空调关   1：空调开
        if (bin.charAt(15) == '1') status.add(GpsStatusType.AirCondition);

        //0：未刹车   1：刹车
        if (bin.charAt(14) == '1') status.add(GpsStatusType.Breaking);

        //0：左转向关  1：左转向开
        //0：未使用GPS卫星进行定位；1：使用GPS卫星进行定位
        //if (bin.charAt(13) == '1') sts.add(GpsStatusType.LeftTurn);

        //0：右转向关  1：右转向开
        //0：未使用北斗卫星进行定位；1：使用北斗卫星进行定位
        //if (bin.charAt(12) == '1') sts.add(GpsStatusType.RightTurn);

        //0：远光关  1：远光开
        if (bin.charAt(11) == '1') status.add(GpsStatusType.DistanceLight);

        //0：近光关 或自定义高3无效 1：近光开或自定义高3有效
        if (bin.charAt(10) == '1') status.add(GpsStatusType.LowLight);

        //0：震动关   1：震动开
        if (bin.charAt(9) == '1') status.add(GpsStatusType.Vibration);

        //0：喇叭关   1：喇叭开
        if (bin.charAt(8) == '1') status.add(GpsStatusType.Horn);

        //0：前门关   1：前门开   //新能源车 使用25解析
        if (bin.charAt(6) == '1') status.add(GpsStatusType.FrontDoor);

        //0：举起   1：未举起    //车斗举升状态2018-2-4-2洛阳渣土设备对接（硬件部门）
        if (bin.charAt(5) == '0') status.add(GpsStatusType.Lift);

        //定位标识:00 无法定位  01有效定位  11 高精度定位
        if (bin.charAt(2) == '0' && bin.charAt(1) == '0') status.add(GpsStatusType.CannotLocation);
        if (bin.charAt(2) == '0' && bin.charAt(1) == '1') status.add(GpsStatusType.Location);
        if (bin.charAt(2) == '1' && bin.charAt(1) == '1') status.add(GpsStatusType.HighPrecisionLocation);

        // 环卫:洒扫状态  0：未扫水清扫   1：扫水清扫中   中科建德:电机工作状态
        if (bin.charAt(2) == '1') status.add(GpsStatusType.Cleaning);

        //位置信息是否是补传
        if (bin.charAt(0) == '1') status.add(GpsStatusType.BlindSpot);

        return status;
    }

    /**
     * 格式化扩展字段
     *
     * @param body
     * @param gps
     */
    public static void ParseGpsExtenstionInfo(byte[] body, GpsData gps) {
        if (body.length > 28) {
            byte[] bs = new byte[body.length - 28];
            System.arraycopy(body, 28, bs, 0, body.length - 28);
            while (bs.length > 0) {
                if (bs.length < 2) break;
                int attachId = DecoderUtils.parseIntFromBytes(bs, 0, 1);
                int attachLen = DecoderUtils.parseIntFromBytes(bs, 1, 1);
                byte[] contentBytes = new byte[attachLen];
                if (attachLen > (bs.length - 2)) return;
                System.arraycopy(bs, 2, contentBytes, 0, attachLen);
                String  attachContent = HexStrUtil.encodeHex(contentBytes);
                int value = 0;
                try {

                    switch (attachId) {
                        case 0x01:
                            //里程，对应车上里程表读数
                            if (Long.parseLong(attachContent,16) > 2147483647 || Long.parseLong(attachContent,16) < 0){  //2147483647  Integer最大值
                                gps.mie = 0;
                            }else {
                                value = Integer.parseInt(attachContent, 16);
                                gps.mie = value * 100;
                            }
                            break;
                        case 0x02:
                            //油量，对应车上油量表读数
                            value = Integer.parseInt(attachContent, 16);
                            if (value > 0)
                                gps.SetProperty(GpsPropertyType.Oil, String.valueOf(value / 10));
                            break;
                        case 0x03:
                            //行驶记录功能获取的速度
                            value = Integer.parseInt(attachContent, 16);
                            if (value > 0)
                                gps.SetProperty(GpsPropertyType.DriveSpeed, String.valueOf(value / 10));
                            break;
                        case 0x04:
                            //转鼓状态：首字节=正反转，1为正转，0为反转；后两字节表示转速，单位：100ms/圈
                            if (Integer.parseInt(attachContent.substring(0, 2), 16) > 0)
                                gps.SetProperty(GpsPropertyType.DrumDirect, String.valueOf(Integer.parseInt(attachContent.substring(0, 2), 16)));

                            if (Integer.parseInt(attachContent.substring(2), 16) > 0)
                                gps.SetProperty(GpsPropertyType.DrumSpeed, String.valueOf(Integer.parseInt(attachContent.substring(2), 16)));
                            break;
                        case 0x11:
                            //超速报警附加信息
                            if (attachLen == 1)
                                gps.SetProperty(GpsPropertyType.OverspeedLocation, String.valueOf(FenceType.GetValue(FenceType.None)));
                            else {
                                gps.SetProperty(GpsPropertyType.OverspeedLocation, String.valueOf(Integer.parseInt(attachContent.substring(0, 2), 16)));
                                gps.SetProperty(GpsPropertyType.OverspeedAreaId, String.valueOf(Integer.parseInt(attachContent.substring(2), 16)));
                            }
                            break;
                        case 0x12:
                            //进出区域/路线报警附加信息
                            gps.SetProperty(GpsPropertyType.AccessareasLocation, String.valueOf(Integer.parseInt(attachContent.substring(0, 2), 16)));
                            gps.SetProperty(GpsPropertyType.AccessareasAreaId, String.valueOf(Integer.parseInt(attachContent.substring(2, 10), 16)));
                            gps.SetProperty(GpsPropertyType.AccessareasDirect, String.valueOf(Integer.parseInt(attachContent.substring(10), 16)));
                            break;
                        case 0x13:
                            //路段行驶时间不足/过长报警附加信息
                            gps.SetProperty(GpsPropertyType.TraveltimeLenOrGreatLineId, String.valueOf(Integer.parseInt(attachContent.substring(0, 8), 16)));
                            gps.SetProperty(GpsPropertyType.TraveltimeLenOrGreatDriveTime, String.valueOf(Integer.parseInt(attachContent.substring(8, 12), 16)));
                            gps.SetProperty(GpsPropertyType.TraveltimeLenOrGreatResult, String.valueOf(Integer.parseInt(attachContent.substring(12), 16)));
                            break;
                        case 0x14:
                            //1078协议视频报警
//                            gps.setVam(Long.parseLong(attachContent.substring(0,8),16));
                            gps.alm.addAll(VideoAlarm(HexStrUtil.byteArrToBinStr(contentBytes, 0, 4)));
                            break;
                        case 0x15:
                            //1078协议视频信号丢失报警
//                            gps.setVam(Long.parseLong(attachContent.substring(0,8),16));
                            gps.SetProperty(GpsPropertyType.videoSignalLose,ParseForVideoMissAlarm(attachContent));
                            break;
                        case 0x16:
                            //1078协议视频信号遮挡报警
                            gps.SetProperty(GpsPropertyType.videoSignalOcclusion,ParseForVideoShelterAlarm(attachContent));
                            break;
//                        case 0x17:
//                            gps.SetProperty(GpsPropertyType.storageUnitFailure, HexStrUtil.byteArrToBinStr(contentBytes, 0, 4));
//                            break;
//                        case 0x18:
//                            gps.alm.addAll(DriverActionAlarm(HexStrUtil.byteArrToBinStr(contentBytes, 0, 2)));
//                            gps.SetProperty(GpsAlarmType.FatigueDegree,String.valueOf(Integer.parseInt(attachContent.substring(4,6),16)));
//                            break;

                        case 0x31:
                            value = Integer.parseInt(attachContent, 16);
                            gps.SetProperty(GpsPropertyType.satelliteNum, String.valueOf(value));
                            break;
                        case 0xE8:
                            //重量传感器值,当值为0xFFFF时表示没有重量回传
                            ParseZtcDeviceStatus(attachContent, gps);
                            break;
                        case 0x25:
                            //新北斗 扩展车辆信号状态位
                            //ParseStatusXinBeidou(gps.sts, contentBytes);
                            break;
                        case 0xE0:
                            //油耗
                            ParseGpsFuelConsumption(gps, attachContent);
                            break;
                        case 0xEB:
//                            if (attachContent.length() == 8) {
//                                //悦心超声波油耗传感器
//                                ParseGpsFuelConsumptionForRx(gps, attachContent);
//                            } else if(contentBytes.length == 22||contentBytes.length == 11){
//                                //未知协议
//                            }else {
//                                //BSJ上行扩展指令
//                                ParseBsjAttach0Xeb(gps, contentBytes);
//                            }
                            //电池电量说明：
                            //长度: 2 类型: WORD
                            //第一个字节代表电池电量,其值为当剩余电量值
                            //第二个字节代表当前是否正在充电
                            //1：充电状态  2：未充电
                            //例如： EB020801 代表当前电池剩余电量为8%，处于充电状态
                            gps.SetProperty(GpsPropertyType.batteryCapacity,String.valueOf(Integer.valueOf(attachContent.substring(0, 2), 16)));
                            gps.SetProperty(GpsPropertyType.charge,String.valueOf(Integer.valueOf(attachContent.substring(2, 4), 16)));

                            break;
                        case 0xEC:
                            //长度: 2 类型: WORD
                            //第一个字节平台设置定位模式
                            //1.基站定位； 2.普通定位； 3.高精度定位+
                            //第二个字节当前定位器定位状态
                            //1：基站定位；2：WIFI定位； 3：GPS定位 4：高精度RTK
                            //例如：EC020301,代表平台设置高精度模式，当前定位状态为基站定位
                            gps.SetProperty(GpsPropertyType.SetPositioningMode,String.valueOf(Integer.valueOf(attachContent.substring(0, 2), 16)));
                            gps.SetProperty(GpsPropertyType.PositioningMode,String.valueOf(Integer.valueOf(attachContent.substring(2, 4), 16)));
                            break;
                        case 0xEF:
                            //电池电压
                            gps.SetProperty(GpsPropertyType.batteryVoltage,String.valueOf(Integer.valueOf(attachContent.substring(0, 4), 16)));

                        case 0xE1:
                            if (attachContent.length() == 8) {
                                //0xE1(温度)
                                ParseGpsHumidity(gps, attachContent);
                            } else {
                                //0xE1:农机扩展
                                // ParseNjDeviceAttach(attachContent, gps);
                            }
                            break;
                        case 0xF1:
                            //油量传感器值
                            gps.SetProperty(GpsPropertyType.OilStatus, String.valueOf(Integer.parseInt(attachContent.substring(0, 2), 16)));
                            gps.SetProperty(GpsPropertyType.OilLevel, String.valueOf(Integer.parseInt(attachContent.substring(2, 6), 16)/10.0));
                            break;
                        //农机深度传感器
                        case 0xF3:
                            ParseForAgricultural(gps,contentBytes);
                            break;
                        //冷库设备温度
                        case 0xF4:
                            TemForAgricultural(gps,contentBytes);
                            break;
                        //冷库设备湿度
                        case 0xF5:
                            gps.SetProperty(GpsPropertyType.hum, String.valueOf(Integer.parseInt(attachContent.substring(0, 2), 16) +
                                    Integer.parseInt(attachContent.substring(2, 4), 16) + Integer.parseInt(attachContent.substring(4, 6), 16)));
                            break;
                        case 0x64:
                            Jsalt12Alarm.AdvancedDriverAssistanceAlarm(gps,contentBytes);
                            break;
                        case 0x65:
                            Jsalt12Alarm.DriverStatusMonitoringAlarm(gps,contentBytes);
                            break;
                        case 0x66:
                            Jsalt12Alarm.TirePressureMonitoringAlarm(gps,contentBytes);
                            break;
                        case 0x67:
                            Jsalt12Alarm.BlindSpotMonitoringAlarm(gps,contentBytes);
                            break;
                        case 0xE3:
                            ParseForDump(gps, attachContent);
                            break;
                        case 0xE4:
                            ParseForAlarm(gps.alm,contentBytes);
                            break;
                        case 0x6A:
                            Jsalt12Alarm.BoxDistinguish(gps,contentBytes);
                            break;
                        case 0x6B:
                            Jsalt12Alarm.BlindArea(gps,contentBytes);
                            break;

                    }
                }catch (Exception e){
                    logger.error("数据处理异常：id:{},{},{}",attachId, HexStrUtil.encodeHex(contentBytes) ,e.getMessage());
                }finally {
                    byte[] b = new byte[bs.length - attachLen - 2];
                    System.arraycopy(bs, 2 + attachLen, b, 0, bs.length - 2 - attachLen);
                    bs = null;
                    bs = b;
                }
            }
        }
    }

    /**
     * 驾驶员行为报警
     */
    private static List<Integer> DriverActionAlarm(String s){
       List<Integer> alarms = new ArrayList<>();
       if (s.charAt(31) == '1')alarms.add(GpsAlarmType.FatigueDriving);
       if (s.charAt(30) == '1')alarms.add(GpsAlarmType.MakingCalls);
       if (s.charAt(20) == '1')alarms.add(GpsAlarmType.Smoking);
       return alarms;
    }


    /**
     * 冷链设备8路温度
     */
    private static void TemForAgricultural(GpsData gpsData,byte[] contentBytes){
        while (contentBytes.length > 0){
            int attachId = DecoderUtils.parseIntFromBytes(contentBytes, 0, 1);
            int attachLen = 5;
            byte[] msgBytes = new byte[attachLen];
            if (attachLen > (contentBytes.length - 1)) return;
            System.arraycopy(contentBytes, 1, msgBytes, 0, attachLen);
            String attachContent = HexStrUtil.encodeHex(contentBytes);
            try {
                switch (attachId){
                    case 0x01:
                        gpsData.SetProperty(GpsPropertyType.Tem1, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                    case 0x02:
                        gpsData.SetProperty(GpsPropertyType.Tem2, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                    case 0x03:
                        gpsData.SetProperty(GpsPropertyType.Tem3, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                    case 0x04:
                        gpsData.SetProperty(GpsPropertyType.Tem4, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                    case 0x05:
                        gpsData.SetProperty(GpsPropertyType.Tem5, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                    case 0x06:
                        gpsData.SetProperty(GpsPropertyType.Tem6, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                    case 0x07:
                        gpsData.SetProperty(GpsPropertyType.Tem7, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                    case 0x08:
                        gpsData.SetProperty(GpsPropertyType.Tem8, HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(msgBytes)));
                        break;
                }

            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                byte[] b = new byte[contentBytes.length - attachLen - 1];
                System.arraycopy(contentBytes, 1 + attachLen, b, 0, contentBytes.length - 1 - attachLen);
                contentBytes = null;
                contentBytes = b;
            }
        }

    }

    /**
     * 复合农机角度深度传感器
     */
    private static void ParseForAgricultural(GpsData gpsData,byte[] contentBytes){
        int attachId = DecoderUtils.parseIntFromBytes(contentBytes, 0, 1);
        int attachLen = DecoderUtils.parseIntFromBytes(contentBytes, 1, 1);
        byte[] msgBytes = new byte[attachLen];
        if (attachLen > (contentBytes.length - 2)) return;
        System.arraycopy(contentBytes, 2, msgBytes, 0, attachLen);
        String attachContent = HexStrUtil.encodeHex(contentBytes);
        try {
            switch (attachId){
                case 0x01:
                    gpsData.SetProperty(GpsPropertyType.AdditionalInformation,HexStrUtil.hexStringToString(HexStrUtil.bytesToHexString(contentBytes)));
                    break;
                case 0x02:
                    gpsData.SetProperty(GpsPropertyType.devNo,attachContent.substring(0,10));
                    break;
                case 0x03:
                    gpsData.SetProperty(GpsPropertyType.workParam,attachContent.substring(0,12));
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    /**
     * 倾倒状态
     */
    private static void ParseForDump(GpsData gps ,String attachContent){
        //附加消息ID：0xE3，附加消息长度：13 前一个字节是倾倒状态位：0x00 正常倾倒，0x01 违规卸载
        //1-6:倾倒开始时间  7-13：倾倒结束时间
        attachContent = attachContent.substring(0, 26);
        int dumpValue = Integer.parseInt(attachContent.substring(0, 2), 16);
        Date startTimeValue = TimeUtil.getDateByFormat(attachContent.substring(2,14), TimeUtil.dateFormatyyMMddHHmmss);
        String str1 = TimeUtil.getStringByFormat(startTimeValue, TimeUtil.dateFormatYMDHMS);
        Date endTimeValue = TimeUtil.getDateByFormat(attachContent.substring(14,26), TimeUtil.dateFormatyyMMddHHmmss);
        String str2 = TimeUtil.getStringByFormat(endTimeValue, TimeUtil.dateFormatYMDHMS);
        if (dumpValue == 0x00 || dumpValue == 0x01){
            gps.SetProperty(GpsPropertyType.dumpAlarm, String.valueOf(dumpValue));
            gps.SetProperty(GpsPropertyType.startTime,str1);
            gps.SetProperty(GpsPropertyType.endTime,str2);
        }
    }

    /**
     * 两种报警
     */
    private static void ParseForAlarm(List<Integer> alarms, byte[] bin){
        String s1 = HexStrUtil.byteArrToBinStr(bin, 0, bin.length);
        String s2 = HexStrUtil.byteArrToBinStr(bin, 0, bin.length);
        //车箱未密闭报警
        if (s1.charAt(7) == '1') alarms.add(GpsPropertyType.unlockAlarm);

        //车辆非法举斗报警
        if (s1.charAt(6) == '1') alarms.add(GpsPropertyType.illegalDumpAlarm);

        //车辆超载报警（保留）
        //if (s1.charAt(5) == '1') alarms.add(GpsAlarmForZhongDuan.overLoadAlarm);

        //超速违规
        if (s1.charAt(4) == '1') alarms.add(GpsPropertyType.speedingAlarm);

        //越界违规
        if (s1.charAt(3) == '1') alarms.add(GpsPropertyType.breakBoundAlarm);

        //保留
        //if (s1.charAt(2) == '1') alarms.add(GpsAlarmForZhongDuan.keep);

        //驾驶员身份不符
        if (s1.charAt(1) == '1') alarms.add(GpsPropertyType.driverAlarm);

        //无证违规
        if (s1.charAt(0) == '1') alarms.add(GpsPropertyType.nolisenseAlarm);

        //闯禁报警
        if (s2.charAt(7) == '1') alarms.add(AlarmForBreak.breakAlarm);
    }

    /**
     * 1078视频报警
     */
    private static List<Integer> VideoAlarm(String s){
        List<Integer> alarms = new ArrayList<Integer>();
        if (s.charAt(31) == '1') alarms.add(GpsPropertyType.videoSignalLose);
        if (s.charAt(30) == '1') alarms.add(GpsPropertyType.videoSignalOcclusion);
        if (s.charAt(29) == '1') alarms.add(GpsPropertyType.storageUnitFailure);
        if (s.charAt(28) == '1') alarms.add(GpsPropertyType.otherDeviceFailure);
        if (s.charAt(27) == '1') alarms.add(GpsPropertyType.carOvercrowding);
        if (s.charAt(26) == '1') alarms.add(GpsPropertyType.abnormalDriving);
        if (s.charAt(25) == '1') alarms.add(GpsPropertyType.thresholdAlarm);
        return alarms;
    }

    /**
     * 视频信号丢失报警信息
     */
    private static String ParseForVideoMissAlarm(String str){
        //16进制字符串转2进制
        String bin = HexStrUtil.hexString2Binary(str);
        List<Integer> alarms = new ArrayList<>();
        // 通道32
        if (bin.charAt(0) == '1') alarms.add(32);

        // 通道31
        if (bin.charAt(1) == '1') alarms.add(31);

        // 通道30
        if (bin.charAt(2) == '1') alarms.add(30);

        // 通道29
        if (bin.charAt(3) == '1') alarms.add(29);

        // 通道28
        if (bin.charAt(4) == '1') alarms.add(28);

        // 通道27
        if (bin.charAt(5) == '1') alarms.add(27);

        // 通道26
        if (bin.charAt(6) == '1') alarms.add(26);

        // 通道25
        if (bin.charAt(7) == '1') alarms.add(25);

        // 通道24
        if (bin.charAt(8) == '1') alarms.add(24);

        // 通道23
        if (bin.charAt(9) == '1') alarms.add(23);

        // 通道22
        if (bin.charAt(10) == '1') alarms.add(22);

        // 通道21
        if (bin.charAt(11) == '1') alarms.add(21);

        // 通道20
        if (bin.charAt(12) == '1') alarms.add(20);

        // 通道19
        if (bin.charAt(13) == '1') alarms.add(19);

        // 通道18
        if (bin.charAt(14) == '1') alarms.add(18);

        // 通道17
        if (bin.charAt(15) == '1') alarms.add(17);

        // 通道16
        if (bin.charAt(16) == '1') alarms.add(16);

        // 通道15
        if (bin.charAt(17) == '1') alarms.add(15);

        // 通道14
        if (bin.charAt(18) == '1') alarms.add(14);

        // 通道13
        if (bin.charAt(19) == '1') alarms.add(13);

        // 通道12
        if (bin.charAt(20) == '1') alarms.add(12);

        // 通道11
        if (bin.charAt(21) == '1') alarms.add(11);

        // 通道10
        if (bin.charAt(22) == '1') alarms.add(10);

        //通道9
        if (bin.charAt(23) == '1') alarms.add(9);

        //通道8
        if (bin.charAt(24) == '1') alarms.add(8);

        //通道7
        if (bin.charAt(25) == '1') alarms.add(7);

        //通道6
        if (bin.charAt(26) == '1') alarms.add(6);

        //通道5
        if (bin.charAt(27) == '1') alarms.add(5);

        //通道4
        if (bin.charAt(28) == '1') alarms.add(4);

        //通道3
        if (bin.charAt(29) == '1') alarms.add(3);

        //通道2
        if (bin.charAt(30) == '1') alarms.add(2);

        //通道1
        if (bin.charAt(31) == '1') alarms.add(1);

        return GsonUtils.getJsonFromObject(alarms);
    }



    /**
     * 视频信号遮挡报警信息
     */
    private static String ParseForVideoShelterAlarm(String str){
        //16进制字符串转2进制
        String bin = HexStrUtil.hexString2Binary(str);
        List<Integer> alarms = new ArrayList<>();
        // 通道32
        if (bin.charAt(0) == '1') alarms.add(32);

        // 通道31
        if (bin.charAt(1) == '1') alarms.add(31);

        // 通道30
        if (bin.charAt(2) == '1') alarms.add(30);

        // 通道29
        if (bin.charAt(3) == '1') alarms.add(29);

        // 通道28
        if (bin.charAt(4) == '1') alarms.add(28);

        // 通道27
        if (bin.charAt(5) == '1') alarms.add(27);

        // 通道26
        if (bin.charAt(6) == '1') alarms.add(26);

        // 通道25
        if (bin.charAt(7) == '1') alarms.add(25);

        // 通道24
        if (bin.charAt(8) == '1') alarms.add(24);

        // 通道23
        if (bin.charAt(9) == '1') alarms.add(23);

        // 通道22
        if (bin.charAt(10) == '1') alarms.add(22);

        // 通道21
        if (bin.charAt(11) == '1') alarms.add(21);

        // 通道20
        if (bin.charAt(12) == '1') alarms.add(20);

        // 通道19
        if (bin.charAt(13) == '1') alarms.add(19);

        // 通道18
        if (bin.charAt(14) == '1') alarms.add(18);

        // 通道17
        if (bin.charAt(15) == '1') alarms.add(17);

        // 通道16
        if (bin.charAt(16) == '1') alarms.add(16);

        // 通道15
        if (bin.charAt(17) == '1') alarms.add(15);

        // 通道14
        if (bin.charAt(18) == '1') alarms.add(14);

        // 通道13
        if (bin.charAt(19) == '1') alarms.add(13);

        // 通道12
        if (bin.charAt(20) == '1') alarms.add(12);

        // 通道11
        if (bin.charAt(21) == '1') alarms.add(11);

        // 通道10
        if (bin.charAt(22) == '1') alarms.add(10);

        //通道9
        if (bin.charAt(23) == '1') alarms.add(9);

        //通道8
        if (bin.charAt(24) == '1') alarms.add(8);

        //通道7
        if (bin.charAt(25) == '1') alarms.add(7);

        //通道6
        if (bin.charAt(26) == '1') alarms.add(6);

        //通道5
        if (bin.charAt(27) == '1') alarms.add(5);

        //通道4
        if (bin.charAt(28) == '1') alarms.add(4);

        //通道3
        if (bin.charAt(29) == '1') alarms.add(3);

        //通道2
        if (bin.charAt(30) == '1') alarms.add(2);

        //通道1
        if (bin.charAt(31) == '1') alarms.add(1);

        return GsonUtils.getJsonFromObject(alarms);
    }






    /**
     * 渣土车重量传感器扩展协议
     */
    private static void ParseZtcDeviceStatus(String attachContent, GpsData gps) {
        /*
         * 附加信息ID：0xE8，附加信息长度：6，前2字节为传感器数值，取值范围0-0xFFFF。
         * 第三个字节为传感器状态。
         * 第四个字节为顶灯状态（0x00为熄灭，0x01为绿灯，0x02为黄灯，0x04为红灯，即该字节低三位分别对应三色车灯开关，0为关，1为开），
         * 第五、六个字节为传感器速度，单位为0.1km/h。
         */
        if (attachContent.length() < 12) {
            attachContent = attachContent.substring(0, 4);
            int weightSerValue = Integer.parseInt(attachContent.substring(0, 4), 16);
            if (weightSerValue != 0xFFFF)
                gps.SetProperty(GpsPropertyType.ZtWeightValue, String.valueOf(weightSerValue));
        } else {
            attachContent = attachContent.substring(0, 12);
            int weightSerValue = Integer.parseInt(attachContent.substring(0, 4), 16);
            int weightSerStatus = Integer.parseInt(attachContent.substring(4, 6), 16);
            int leightStatus = Integer.parseInt(attachContent.substring(6, 8), 16);
            double speed = Double.parseDouble(Integer.parseInt(attachContent.substring(8, 10), 16) + "." + Integer.parseInt(attachContent.substring(10, 12), 16));

            if (weightSerValue != 0xFFFF)
                gps.SetProperty(GpsPropertyType.ZtWeightValue, String.valueOf(weightSerValue));

            //工作模式 0x00正常模式；0x01debug模式
            gps.SetProperty(GpsPropertyType.ZtWeightStatus, String.valueOf(weightSerStatus));
            if (speed > 0)
                gps.SetProperty(GpsPropertyType.ZtSensorSpeed, String.valueOf(speed));

            boolean leightRedOn = (leightStatus & 0x04) != 0x00;
            boolean leightGreenOn = (leightStatus & 0x01) != 0x00;
            boolean leightYelloOn = (leightStatus & 0x02) != 0x00;
            //红灯
            gps.SetProperty(GpsPropertyType.ZtLeightRedOn, String.valueOf(leightRedOn));
            //绿灯
            gps.SetProperty(GpsPropertyType.ZtLeightGreenOn, String.valueOf(leightGreenOn));
            //黄灯
            gps.SetProperty(GpsPropertyType.ZtLeightYelloOn, String.valueOf(leightYelloOn));
            //顶灯状态
            gps.SetProperty(GpsPropertyType.ZtLeightOn, String.valueOf(leightRedOn || leightGreenOn || leightYelloOn));

            if (attachContent.length() >= 16)    //Integer.parseInt(attachContent.substring(0, 4),16);
            {
                //传感器原始值
                gps.SetProperty(GpsPropertyType.ZtSensorRawData, attachContent.substring(12, 16));
            }
            if (attachContent.length() >= 32) {
                //满载阈值
                gps.SetProperty(GpsPropertyType.ZtFullLoadThreshold, String.valueOf(Integer.parseInt(attachContent.substring(18, 22), 16)));
                //超载阈值
                gps.SetProperty(GpsPropertyType.ZtOverLoadThreshold, String.valueOf(Integer.parseInt(attachContent.substring(22, 26), 16)));
                //严重超载阈值
                gps.SetProperty(GpsPropertyType.ZtSevereOverloadThreshold, String.valueOf(Integer.parseInt(attachContent.substring(26, 30), 16)));
            }
            if (attachContent.length() >= 64) {
                //新版载重及 举升传感器
                gps.SetProperty(GpsPropertyType.ZtLiftingStatus, String.valueOf(Integer.parseInt(attachContent.substring(32, 34), 16) == (1)));
                gps.SetProperty(GpsPropertyType.ZtWeightStatus, String.valueOf(Integer.parseInt(attachContent.substring(34, 36), 16)));
                //装载装载勺数
                gps.SetProperty(GpsPropertyType.ZtLoadedspoons, String.valueOf(Integer.parseInt(attachContent.substring(36, 38), 16)));
                //数据标识
                gps.SetProperty(GpsPropertyType.ZtWeightMark, String.valueOf(Integer.parseInt(attachContent.substring(38, 40), 16)));
            }
        }

    }


    /**
     * 新北斗 扩展车辆信息状态
     *
     * @param status
     * @param
     */
    private static void ParseStatusXinBeidou(List<Integer> status, byte[] bin) {
        String s = HexStrUtil.byteArrToBinStr(bin, 0, bin.length);
        if (s.charAt(31) == 1)
            status.add(GpsStatusType.LowLight);

        if (s.charAt(30) == 1)
            status.add(GpsStatusType.DistanceLight);

        if (s.charAt(29) == 1)
            status.add(GpsStatusType.RightTurn);

        if (s.charAt(28) == 1)
            status.add(GpsStatusType.LeftTurn);

        if (s.charAt(27) == 1)
            status.add(GpsStatusType.Breaking);
    }



    /**
     * 油耗、湿度、油杆
     *
     * @param gps
     * @param content
     */
    private static void ParseGpsFuelConsumption(GpsData gps, String content) {
        if (content.length() != 8) return;
            /*
             XX YY YY YY
            Byte1: 油耗类型，=0:电阻式，=1:电压式 =2：液位值
            Byte2~Byte4:
                 电阻式：以欧姆为单位, 第一，二个字节表示十六进制的整数，第三个字节表示小数），数制：十六进制。
             比如：0x01,0x2A,0x06表示298.6欧姆
                 电压式：以伏特为单位, 第一字节高4位为电压整数，其余为小数位。
            数制：BCD码，比如：0x45,0x67,0x89表示4.56789V
                 液位式：以0.1mm为单位,比如：0x00,0x2A,0x06表示1084.8mm

             */
        int fuelType = Integer.parseInt(content.substring(0, 2), 16);
        gps.SetProperty(GpsPropertyType.FuelType, String.valueOf(fuelType));
        double fuelValue = 0;
        switch (fuelType) {
            case 0:
                //电阻式：以欧姆为单位, 第一，二个字节表示十六进制的整数，第三个字节表示小数），数制：十六进制。
                fuelValue = Double.parseDouble(Integer.parseInt(content.substring(2, 6), 16) + "." + Integer.parseInt(content.substring(6, 8), 16));
                break;
            case 1:
                //电压式：以伏特为单位, 第一字节高4位为电压整数，其余为小数位。
                fuelValue = Double.parseDouble(Integer.parseInt(content.substring(2, 4), 16) + "." + Integer.parseInt(content.substring(4, 8), 16));
                break;
            case 2:
                //液位式：以0.1mm为单位,比如：0x00,0x2A,0x06表示1084.8mm
                fuelValue = Double.parseDouble(String.valueOf(Integer.parseInt(content.substring(2, 8), 16)));
                break;
            default:
                break;
        }
        gps.SetProperty(GpsPropertyType.FuelValue, String.valueOf(fuelValue));
    }


    /**
     * 格式化湿度
     *
     * @param gps
     * @param content
     */
    private static void ParseGpsHumidity(GpsData gps, String content) {
            /*
             JJ JJ XX XX YY YY ZZ ZZ
                支持4路温度，每路两字节，第一字节为整数，第二字节为小数
                当温度为负数时，第一字节的最高位为1。
                如：9A 05 = -26.5度， 30 05 = 48.5度
             */
        if (content.length() != 16) return;
        String[] vals = new String[4];
        vals[0] = Integer.parseInt(content.substring(0, 2), 16) + "." + Integer.parseInt(content.substring(2, 4), 16);
        vals[1] = Integer.parseInt(content.substring(4, 6), 16) + "." + Integer.parseInt(content.substring(6, 8), 16);
        vals[2] = Integer.parseInt(content.substring(8, 10), 16) + "." + Integer.parseInt(content.substring(10, 12), 16);
        vals[3] = Integer.parseInt(content.substring(12, 14), 16) + "." + Integer.parseInt(content.substring(14, 16), 16);
        gps.SetProperty(GpsPropertyType.Humidity, StringUtils.join(vals, ","));

    }

    /**
     * 悦心超声波油耗传感器
     *
     * @param gps
     * @param content
     */
    private static void ParseGpsFuelConsumptionForRx(GpsData gps, String content) {
        if (content.length() < 8) return;
        //悦心超声波油耗传感器
        byte[] bytes = HexStrUtil.decodeHex(content.substring(8));
        gps.SetProperty(GpsPropertyType.FuelType, "4");
        gps.SetProperty(GpsPropertyType.FuelValue, String.valueOf(HexStrUtil.bytes2Double(bytes)));
    }


    /**
     * BSJ扩展数据格式，兼容2929扩展协议，详见BSJ扩展附加D表    自定义
     *
     * @param gps
     * @param contentBytes
     */
    private static void ParseBsjAttach0Xeb(GpsData gps, byte[] contentBytes) {
        int exLen = 0;
        int dataIndex = 0;
        byte[] dataBytes = null;
        do {
            byte[] b1 = new byte[2];
            System.arraycopy(contentBytes, dataIndex + 2, b1, 0, 2);
            int status = Integer.parseInt(HexStrUtil.encodeHex(b1), 16);
            switch (status) {
                case 0x0001:
                    //油耗数据段
                        /*3字节油耗数据b1 b2 b3
    油耗值以欧姆为单位，第一，二个字节表示十六进制的整数，第三个字节表示小数，比如:0x01,0x2A,0x06BIAOSHI  298.6欧姆
    */
                    exLen = 0x0003;
                    dataBytes = new byte[exLen];
                    System.arraycopy(contentBytes, dataIndex + 4, dataBytes, 0, exLen);
                    gps.SetProperty(GpsPropertyType.FuelType, "0");
                    gps.SetProperty(GpsPropertyType.FuelValue, String.valueOf(HexStrUtil.bytes2Double(dataBytes)));
                    break;
                case 0x0003:
                    //温控数据段
                    exLen = 0x000A - 2;
                    dataBytes = new byte[exLen];
                    System.arraycopy(contentBytes, dataIndex + 4, dataBytes, 0, exLen);
                    break;
                case 0x0004:
                    //压力油耗数据
                    exLen = 0x0005 - 2;
                    dataBytes = new byte[exLen];
                    System.arraycopy(contentBytes, dataIndex + 4, dataBytes, 0, exLen);
                    gps.SetProperty(GpsPropertyType.FuelType, "1");
                    gps.SetProperty(GpsPropertyType.FuelValue, String.valueOf(HexStrUtil.bytes2Double(dataBytes)));
                    break;
                case 0x0023:
                    //带通信的油位传感器
                    exLen = 0x0008 - 2;
                    dataBytes = new byte[exLen];
                    System.arraycopy(contentBytes, dataIndex + 4, dataBytes, 0, exLen);
                    break;
                case 0x00A9:
                    //多基站信息
                    exLen = 0x0024 - 2;
                    dataBytes = new byte[exLen];
                    System.arraycopy(contentBytes, dataIndex + 4, dataBytes, 0, exLen);
                    break;
                case 0x0089:
                    //扩展报警状态
                    exLen = 0x0006 - 2;
                    dataBytes = new byte[exLen];
                    System.arraycopy(contentBytes, dataIndex + 4, dataBytes, 0, exLen);
                    String binVals = HexStrUtil.encodeHex(dataBytes, 0, 4);
                    gps.SetProperty(GpsPropertyType.BsjBatterySwitch, binVals.charAt(0) + "");
                    gps.SetProperty(GpsPropertyType.BsjDeviceStatus, binVals.charAt(1) + "");
                    gps.SetProperty(GpsPropertyType.BsjPseudobasestationCheck, binVals.charAt(30) + "");
                    gps.SetProperty(GpsPropertyType.BsjPseudobasestationAlarm, binVals.charAt(31) + "");
                    break;
                case 0x00B2:
                    //SIM ICCID号
                    exLen = 0x000C - 2;
                    dataBytes = new byte[exLen];
                    System.arraycopy(contentBytes, dataIndex + 4, dataBytes, 0, exLen);
                    gps.SetProperty(GpsPropertyType.SimIccid, HexStrUtil.encodeHex(dataBytes));
                    break;
            }
            dataIndex += (exLen + 4);
        } while (dataIndex < contentBytes.length);

    }

    /// <summary>
    /// 油杆
    /// </summary>
    /// <param name="gps"></param>
    /// <param name="content"></param>
    private static void ParseGpsRod(GpsData gps, String content) {

    }

}
